// middleware 目录下的每一个文件都是一个中间件，都需要导出一个 async 函数
// 必须导出一个函数，这是框架规定的
module.exports = () => {
    // 一定要导出一个 async 函数，表示中间件执行函数，和我们的 Koa 是一样的
    return async (ctx, next) => {
        // 如果是登录接口，说明就是去登录获取 token，所以不需要校验 token
        if (['/login', '/code'].includes(ctx.path)) {
            await next();
            return;
        }
        // 其他接口都需要校验 token
        // 1、从请求头（header）中读取 token
        const { token } = ctx.headers;
        // 2、判断是否存在 token
        if (!token) {
            ctx.body = {
                code: 401,
                message: '你没有 token 去乖乖的登录吧',
            };
            return;
        }
        // 3、校验 token 合法性
        // 校验 token 如果不合法会抛出错误，这是我们希望处理的错误，所以就使用 try catch 捕获
        try {
            // 校验 token，如果成功，就会返回在登录时保存到 token 中的信息
            // 如果失败，就会抛出错误，我们会去错误中进行处理
            const user = ctx.helper.checkToken(token);
            // 如果走到这里，说明 token 解析成功了，能拿到 user 信息了
            // 这时，我们将其挂载到 ctx 上下文对象上，方便后面的 controller service 中去使用
            ctx.user = user;
        } catch (error) {
            // token 过期
            if (error.name === 'TokenExpiredError') {
                ctx.body = {
                    code: 401,
                    message: 'token 过期了，你再去登录拿一个吧',
                };
                return;
            }

            // token 不合法
            if (error.name === 'JsonWebTokenError') {
                ctx.body = {
                    code: 401,
                    message: 'token 不合法，你是乱写的',
                };
                return;
            }

            // 其他认证错误，就统一返回登录异常，不再继续细化了
            ctx.body = {
                code: 401,
                message: '登录异常，token 解析失败',
            };
            return;
        }
        // 走到这，说明都校验成功了，就继续吧，此时，ctx 上已经可以拿到 user 信息了
        await next();
    };
};
